import { EventEmitter } from "events";
import path from "node:path";
import { type BrowserWindow } from "electron";
import * as child_process from "node:child_process";
import { IS_DEV } from "@elec/main/util/env";
import type { UnzipCompleteArgs, UnzipErrorArgs } from "@common/archives-file-types";
import fs from "node:fs";
import fsp from "node:fs/promises";
import { getAssetPath } from "@elec/main/util/assets.ts";

/**
 * 解压任务
 */
export class UnzipTask extends EventEmitter {
  private static readonly _7zBin = UnzipTask.get7zBin();

  private static get7zBin() {
    return getAssetPath(`thirdparty/7z/${process.platform}/7za`);
  }

  private _proc?: child_process.ChildProcess;

  constructor(
    readonly id: string,
    readonly archvieFile: string,
    readonly outputDir: string,
    readonly wind: BrowserWindow | undefined,
  ) {
    super();
  }

  /**
   * 根据不同类型压缩包返回解压命令和参数
   * @private
   */
  private _getUnzipCmdAndArgs() {
    const filename = path.basename(this.archvieFile).toLowerCase();
    if (filename.endsWith(".tar.gz") || filename.endsWith(".tgz")) {
      return { cmd: "tar", args: ["-xzf", this.archvieFile, "-C", this.outputDir] };
    }

    return { cmd: UnzipTask._7zBin, args: ["x", this.archvieFile, "-o" + this.outputDir, "-y"] };
  }

  start() {
    const { cmd, args } = this._getUnzipCmdAndArgs();
    const proc = child_process.spawn(cmd, args, {
      windowsHide: true,
      stdio: "inherit",
    });
    proc.on("error", err => {
      this.emit("error", err);
      this.wind?.webContents?.send(`unzip.error.${this.id}`, { message: err.message } as UnzipErrorArgs);
      console.error("[unzip-task] unzip error", err);
    });
    proc.on("exit", code => {
      if (code === 0) {
        this.emit("complete");
        this.wind?.webContents?.send(`unzip.complete.${this.id}`, {
          sourceFile: this.archvieFile,
          fileDir: this.outputDir,
        } as UnzipCompleteArgs);
      }
    });
    this._proc = proc;
    if (IS_DEV) {
      console.log("[unzip-task] start unzip task => taskId:", this.id, this.archvieFile, ">", this.outputDir);
    }
  }

  cancel() {
    if (!this._proc) return;
    this._proc.kill();
    this._proc = undefined;
    if (fs.existsSync(this.outputDir)) {
      setTimeout(() => {
        fsp.rm(this.outputDir, { recursive: true, force: true }).catch(() => void 0);
      }, 200);
    }
  }

  private static readonly _tasks = new Map<string, UnzipTask>();

  static submit(id: string, archvieFile: string, outputDir: string, wind?: BrowserWindow) {
    if (this._tasks.has(id)) {
      return;
    }
    const task = new UnzipTask(id, archvieFile, outputDir, wind);
    this._tasks.set(id, task);
    task.on("complete", () => this._tasks.delete(id));
    task.start();
  }

  static cancel(id: string) {
    const task = this._tasks.get(id);
    task?.cancel();
  }
}
